import type { Ref } from 'vue'
import { isFunction, isUndefined, set } from 'lodash-es'

import { nextTick, ref } from 'vue'

type GetDataByRequest<T> = T extends (...args: any[]) => Promise<infer R>
  ? R extends { list?: infer D }
    ? D
    : never
  : never

type DefaultParams = Record<string, any>

type RunParams<T extends PromiseFn, P> =
  | (Parameters<T>[0] & Partial<P>)
  | Parameters<T>[0]

export interface UseListRequestOptions<T, K extends Ref<DefaultParams>> {
  loading?: Ref<boolean>
  noPagination?: boolean
  params?: K
  buildParams?: (params: DefaultParams) => DefaultParams
  afterFn?: (list: GetDataByRequest<T>) => void
  props?: {
    current: number
    pageSize: number
  }
  immediate?: boolean
}

export interface UseListRequestReturn<
  T extends PromiseFn,
  U extends DefaultParams,
> {
  loading: Ref<boolean>
  run: (params?: RunParams<T, U>, ...args: any[]) => Promise<void>
  silentRun: (params?: RunParams<T, U>, ...args: any[]) => Promise<void>
  data: Ref<GetDataByRequest<T>>

  pagination: Ref<{
    total: number
    current: number
    pageSize: number
  }>
}

/**
 * 通用list请求函数
 * @param fn 请求函数
 * @param params 默认可选参数
 */
export function useListRequest<
  T extends PromiseFn,
  U extends Ref<DefaultParams>,
>(fn?: T, options?: UseListRequestOptions<T, U>): UseListRequestReturn<T, U> {
  const loading = ref(false)
  const data = ref([]) as Ref<GetDataByRequest<T>>
  const pagination = ref<{
    total: number
    current: number
    pageSize: number
  }>({
    current: 1,
    pageSize: 20,
    total: 0,
  })

  const _run = async (params?: RunParams<T, U>, ...args: any[]) => {
    try {
      await nextTick()
      const { current, pageSize } = pagination.value
      const offset = ((current || 1) - 1) * (pageSize || 20)

      const query: Recordable = {
        offset,
        limit: pageSize,
        ...toValue(options?.params || {}),
        ...toValue(params || {}),
      }

      if (options?.noPagination) {
        set(query, 'offset', undefined)
        set(query, 'limit', undefined)
      }

      const res = await fn?.(
        isFunction(options?.buildParams) ? options.buildParams(query) : query,
        ...args,
      )

      data.value = res.list
      pagination.value.total = +res.total
      options?.afterFn?.(res?.list)
    } finally {
      loading.value = false
      if (!isUndefined(options?.loading)) {
        options.loading.value = false
      }
    }
  }

  // 联动loading，常规用法
  const run = async (params?: RunParams<T, U>, ...args: any[]) => {
    loading.value = true
    if (!isUndefined(options?.loading)) {
      options.loading.value = true
    }
    await _run(params, ...args)
  }

  // 无需触发loading，静默执行
  const silentRun = async (params?: RunParams<T, U>, ...args: any[]) => {
    await _run(params, ...args)
  }

  onMounted(() => {
    pagination.value = { ...pagination.value, ...(options?.props || {}) }
    if (options?.immediate) {
      run()
    }
  })
  return { loading, run, silentRun, data, pagination }
}
